1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.collect;
18
19 import static com.google.common.collect.Lists.transform;
20 import static com.google.common.collect.Sets.newHashSet;
21 import static com.google.common.collect.Sets.newTreeSet;
22 import static java.lang.reflect.Modifier.isPublic;
23 import static java.lang.reflect.Modifier.isStatic;
24
25 import com.google.common.base.Function;
26 import com.google.common.base.Joiner;
27 import com.google.common.base.Objects;
28
29 import junit.framework.TestCase;
30
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Type;
33 import java.lang.reflect.TypeVariable;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40
41
42
43
44
45
46
47
48 public class FauxveridesTest extends TestCase {
49 public void testImmutableBiMap() {
50 doHasAllFauxveridesTest(ImmutableBiMap.class, ImmutableMap.class);
51 }
52
53 public void testImmutableListMultimap() {
54 doHasAllFauxveridesTest(
55 ImmutableListMultimap.class, ImmutableMultimap.class);
56 }
57
58 public void testImmutableSetMultimap() {
59 doHasAllFauxveridesTest(
60 ImmutableSetMultimap.class, ImmutableMultimap.class);
61 }
62
63 public void testImmutableSortedMap() {
64 doHasAllFauxveridesTest(ImmutableSortedMap.class, ImmutableMap.class);
65 }
66
67 public void testImmutableSortedSet() {
68 doHasAllFauxveridesTest(ImmutableSortedSet.class, ImmutableSet.class);
69 }
70
71 public void testImmutableSortedMultiset() {
72 doHasAllFauxveridesTest(ImmutableSortedMultiset.class, ImmutableMultiset.class);
73 }
74
75
76
77
78
79
80
81 public void testImmutableSortedMapCopyOfMap() {
82 Map<Object, Object> original =
83 ImmutableMap.of(new Object(), new Object(), new Object(), new Object());
84
85 try {
86 ImmutableSortedMap.copyOf(original);
87 fail();
88 } catch (ClassCastException expected) {
89 }
90 }
91
92 public void testImmutableSortedSetCopyOfIterable() {
93 Set<Object> original = ImmutableSet.of(new Object(), new Object());
94
95 try {
96 ImmutableSortedSet.copyOf(original);
97 fail();
98 } catch (ClassCastException expected) {
99 }
100 }
101
102 public void testImmutableSortedSetCopyOfIterator() {
103 Set<Object> original = ImmutableSet.of(new Object(), new Object());
104
105 try {
106 ImmutableSortedSet.copyOf(original.iterator());
107 fail();
108 } catch (ClassCastException expected) {
109 }
110 }
111
112 private void doHasAllFauxveridesTest(Class<?> descendant, Class<?> ancestor) {
113 Set<MethodSignature> required = getAllRequiredToFauxveride(ancestor);
114 Set<MethodSignature> found = getAllFauxveridden(descendant, ancestor);
115 required.removeAll(found);
116
117 assertEquals("Must hide public static methods from ancestor classes",
118 Collections.emptySet(), newTreeSet(required));
119 }
120
121 private static Set<MethodSignature> getAllRequiredToFauxveride(Class<?> ancestor) {
122 return getPublicStaticMethodsBetween(ancestor, Object.class);
123 }
124
125 private static Set<MethodSignature> getAllFauxveridden(
126 Class<?> descendant, Class<?> ancestor) {
127 return getPublicStaticMethodsBetween(descendant, ancestor);
128 }
129
130 private static Set<MethodSignature> getPublicStaticMethodsBetween(
131 Class<?> descendant, Class<?> ancestor) {
132 Set<MethodSignature> methods = newHashSet();
133 for (Class<?> clazz : getClassesBetween(descendant, ancestor)) {
134 methods.addAll(getPublicStaticMethods(clazz));
135 }
136 return methods;
137 }
138
139 private static Set<MethodSignature> getPublicStaticMethods(Class<?> clazz) {
140 Set<MethodSignature> publicStaticMethods = newHashSet();
141
142 for (Method method : clazz.getDeclaredMethods()) {
143 int modifiers = method.getModifiers();
144 if (isPublic(modifiers) && isStatic(modifiers)) {
145 publicStaticMethods.add(new MethodSignature(method));
146 }
147 }
148
149 return publicStaticMethods;
150 }
151
152
153 private static Set<Class<?>> getClassesBetween(
154 Class<?> descendant, Class<?> ancestor) {
155 Set<Class<?>> classes = newHashSet();
156
157 while (!descendant.equals(ancestor)) {
158 classes.add(descendant);
159 descendant = descendant.getSuperclass();
160 }
161
162 return classes;
163 }
164
165
166
167
168
169
170
171 private static final class MethodSignature
172 implements Comparable<MethodSignature> {
173 final String name;
174 final List<Class<?>> parameterTypes;
175 final TypeSignature typeSignature;
176
177 MethodSignature(Method method) {
178 name = method.getName();
179 parameterTypes = Arrays.asList(method.getParameterTypes());
180 typeSignature = new TypeSignature(method.getTypeParameters());
181 }
182
183 @Override public boolean equals(Object obj) {
184 if (obj instanceof MethodSignature) {
185 MethodSignature other = (MethodSignature) obj;
186 return name.equals(other.name)
187 && parameterTypes.equals(other.parameterTypes)
188 && typeSignature.equals(other.typeSignature);
189 }
190
191 return false;
192 }
193
194 @Override public int hashCode() {
195 return Objects.hashCode(name, parameterTypes, typeSignature);
196 }
197
198 @Override public String toString() {
199 return String.format("%s%s(%s)",
200 typeSignature, name, getTypesString(parameterTypes));
201 }
202
203 @Override public int compareTo(MethodSignature o) {
204 return toString().compareTo(o.toString());
205 }
206 }
207
208 private static final class TypeSignature {
209 final List<TypeParameterSignature> parameterSignatures;
210
211 TypeSignature(TypeVariable<Method>[] parameters) {
212 parameterSignatures =
213 transform(Arrays.asList(parameters),
214 new Function<TypeVariable<?>, TypeParameterSignature>() {
215 @Override
216 public TypeParameterSignature apply(TypeVariable<?> from) {
217 return new TypeParameterSignature(from);
218 }
219 });
220 }
221
222 @Override public boolean equals(Object obj) {
223 if (obj instanceof TypeSignature) {
224 TypeSignature other = (TypeSignature) obj;
225 return parameterSignatures.equals(other.parameterSignatures);
226 }
227
228 return false;
229 }
230
231 @Override public int hashCode() {
232 return parameterSignatures.hashCode();
233 }
234
235 @Override public String toString() {
236 return (parameterSignatures.isEmpty())
237 ? ""
238 : "<" + Joiner.on(", ").join(parameterSignatures) + "> ";
239 }
240 }
241
242 private static final class TypeParameterSignature {
243 final String name;
244 final List<Type> bounds;
245
246 TypeParameterSignature(TypeVariable<?> typeParameter) {
247 name = typeParameter.getName();
248 bounds = Arrays.asList(typeParameter.getBounds());
249 }
250
251 @Override public boolean equals(Object obj) {
252 if (obj instanceof TypeParameterSignature) {
253 TypeParameterSignature other = (TypeParameterSignature) obj;
254
255
256
257
258 return bounds.equals(other.bounds);
259 }
260
261 return false;
262 }
263
264 @Override public int hashCode() {
265 return bounds.hashCode();
266 }
267
268 @Override public String toString() {
269 return (bounds.equals(ImmutableList.of(Object.class)))
270 ? name
271 : name + " extends " + getTypesString(bounds);
272 }
273 }
274
275 private static String getTypesString(List<? extends Type> types) {
276 List<String> names = transform(types, SIMPLE_NAME_GETTER);
277 return Joiner.on(", ").join(names);
278 }
279
280 private static final Function<Type, String> SIMPLE_NAME_GETTER =
281 new Function<Type, String>() {
282 @Override
283 public String apply(Type from) {
284 if (from instanceof Class) {
285 return ((Class<?>) from).getSimpleName();
286 }
287 return from.toString();
288 }
289 };
290 }